home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / networking / news / readers / nn / nn6.4.patch15 < prev    next >
Encoding:
Text File  |  1991-04-12  |  59.0 KB  |  2,134 lines

  1.          This is an official patch to nn release 6.4
  2.          -------------------------------------------
  3.  
  4.                   PATCH #15
  5.  
  6.                 Priority: LOW
  7.  
  8.  
  9. This patch adds a CONSOLIDATED MENU mode to nn.  When a consolidated
  10. menu is used each different subject (or "thread") is only shown once
  11. on the menu by combining all articles with identical subjects into a
  12. single line.  Since only one menu line is used even for articles with
  13. follow-ups, the number of articles presented on each menu page can be
  14. increased significantly.
  15.  
  16. To turn on the consolidated menu feature, just add the following line
  17. to your init file:
  18.     set consolidated-menu
  19.  
  20. When consolidated menus are used, nn operates with two kinds of
  21. subjects: closed and open.
  22.  
  23. An open subject is a subject which is shown in the traditional way
  24. with one menu line for each article with the given subject.  In other
  25. words, when consolidated menus are not used, all subjects are open (by
  26. default).
  27.  
  28. A closed subject is a multi-article subject which is presented by a
  29. single menu line.  This line will be the normal menu line for the
  30. first (oldest) article with the subject, but with the subject field
  31. annotated with a bracketed number showing the number of articles with
  32. that subject, e.g.
  33.  
  34.     a Kim F. Storm     12  [4] Future plans for nn 6.4
  35.     b.Kim F. Storm     43  [3] More plans for nn 6.4
  36.  
  37. In this example, there are four unread articles with subject `a' of
  38. which the first is posted by me and has 12 lines.  The rest of the
  39. articles are hidden, and will only be shown on request.
  40.  
  41. The `.' marker on subject `b' shows that all three articles within
  42. that subject have been read or seen (to simplify presentation on the
  43. menu, the `seen' attribute is treated as the `read' attribute within a
  44. closed subject; they are still handled separately when necessary, e.g.
  45. when updating .newsrc and by the junk-articles command).
  46.  
  47. To select (or deselect) ALL the articles within a closed subject,
  48. simply select the article shown on the menu; this will automatically
  49. select (or deselect) the hidden *unread* articles (by default; see
  50. auto-select-closed for changing this behaviour).  When all the unread
  51. articles within a closed subject are selected, the menu line will be
  52. high-lighted.
  53.  
  54. If you want to view the individual articles in a subject (maybe to
  55. select individual articles depending on the sender's name), you can
  56. open the subject with the commands:
  57.     (x    open subject x on menu
  58.     ((    open current subject
  59.  
  60. When you have completed viewing the opened subject, you can close it
  61. again using the commands:
  62.     )x    close subject x on menu (x is any article with the subject)
  63.     ))    close current subject
  64.  
  65. In the basic layout of the menu line for a closed subject as shown
  66. above, ALL articles in the closed subject are supposed to be either:
  67.  
  68.   unread    menu line is not high-lighted
  69.   selected    menu line is fully high-lighted (if all UNREAD are selected)
  70.   read/seen    there is a '.' (read attribute) following the article id
  71.  
  72. If this is not the case, i.e. there are a mixture of unread, selected,
  73. and read articles, the bracketed number will have one of the following
  74. formats:
  75.   [U:T]        there are U unread articles of T total (U<T)
  76.   [S/T]        there are S selected articles of T total (S<U=T)
  77.   [S/U:T]    there are S selected of U unread of T total (S<U<T)
  78. If there are any selected articles, the information between the
  79. brackets will be high-lighted (to show that something is selected, but
  80. not all the unread articles).
  81.  
  82. The current presentation is a compromise between keeping the `look and
  83. feel' of a closed subject as close to existing customs in the normal
  84. cases, and at the same time give as much information in as little
  85. space as possible in the lesser frequent cases.  Personally, I think
  86. it is a bit messy, so I am very interested in how you feel about it.
  87.  
  88. Notice: Consolidated menus only work with the `subject' and `lexical'
  89. sorting modes.
  90.  
  91. Variables related to consolidated menus are:
  92.  
  93. consolidated-menu    boolean = off    turn consolidation on and off.
  94.  
  95. auto-select-closed    integer    = 1    how much should `x' select:
  96.     0: select root article only
  97.     1: select UNREAD articles only (select all if no unread)
  98.     2: select ALL articles.
  99.  
  100. save-closed-mode    integer = 13    how to save a closed subject `Sx':
  101.     0: save root article (the one on the menu) only
  102.     1: save selected articles within subject
  103.     2: save unread (excl selected) articles within subject
  104.     3: save selected+unread articles within subject
  105.     4: save all articles within subject
  106.     10+<value above>: ask what to do with <> as default choice.
  107.  
  108. counter-delim-left    string = "["    left delimiter on subject counter(s)
  109.  
  110. counter-delim-right    string = "] "    right delimiter on subject counter(s)
  111.  
  112. ----
  113.  
  114. A few other features introduced in this patch are:
  115.  
  116. There is a new variable influencing the look of the menu:
  117.  
  118. menu-spacing        integer    = 0    add blank lines between menu lines:
  119.     0:    no blank lines
  120.     1:    add blank line between subjects (try this with open subjects)
  121.     2:    add blank line between articles
  122.  
  123.  
  124. For all practical purposes, `.' {select} can now be used to reference
  125. the current article on the menu, specifically when nn asks " from
  126. article".  E.g. to have ^Xk add the current subject to the kill file,
  127. the following macro can be defined:
  128.  
  129.     make map ctl-x            # create keymap for ^X prefix
  130.     map menu ^x prefix ctl-x    # map ^X to the new keymap
  131.  
  132.     map ctl-x k (
  133.         kill-select
  134.         '^m'            # (CR => kill subject for 30 days)
  135.         select            # current subject
  136.     )
  137.  
  138. As usual, all changes are described in the updated RELEASE_NOTES file
  139. (read that for more details about this patch).  Thanks to all who
  140. reported bugs and provided fixes.
  141.  
  142. To apply this patch, use nn's :patch command, or run this command from
  143. the shell in the root of the nn source tree:
  144.     patch -p0 < this-article
  145.  
  146. Then run "make all" and "./inst u".
  147.  
  148. ++Kim Storm
  149.  
  150. ===================================================================
  151.  
  152. *** ./LAST/data.h    Wed May  2 23:10:48 1990
  153. --- data.h    Thu Apr 11 22:11:26 1991
  154. ***************
  155. *** 152,163 ****
  156.       char *    sender;        /* sender's name        */
  157.       char *    subject;    /* subject (w/o Re:)        */
  158.   
  159. -     int16    replies;    /* no of Re:            */
  160.       int16    lines;        /* no of lines        */
  161.   
  162.       int8    subj_length;    /* length of subject        */
  163.       int8    name_length;    /* length of sender        */
  164.   
  165.       attr_type    attr;        /* attributes:             */
  166.       attr_type    disp_attr;    /* currently displayed attr.    */
  167.       /* warning: notice relation between A_SELECT and A_AUTO_SELECT */
  168. --- 152,165 ----
  169.       char *    sender;        /* sender's name        */
  170.       char *    subject;    /* subject (w/o Re:)        */
  171.   
  172.       int16    lines;        /* no of lines        */
  173. +     int8    replies;    /* no of Re:            */
  174.   
  175.       int8    subj_length;    /* length of subject        */
  176.       int8    name_length;    /* length of sender        */
  177.   
  178. +     int8    menu_line;    /* current line on menu        */
  179.       attr_type    attr;        /* attributes:             */
  180.       attr_type    disp_attr;    /* currently displayed attr.    */
  181.       /* warning: notice relation between A_SELECT and A_AUTO_SELECT */
  182. ***************
  183. *** 177,182 ****
  184. --- 179,185 ----
  185.   
  186.   #    define A_SAME            FLAG(1)    /* same subject as prev. article */
  187.   #    define A_ALMOST_SAME    FLAG(2)    /* A_SAME (match-limit)        */
  188. + #    define A_ROOT_ART    FLAG(3)    /* root article in subject    */
  189.   #    define A_NEXT_SAME    FLAG(4)    /* next is same subject        */
  190.   #    define A_DIGEST       FLAG(5)    /* digest sub article        */
  191.   #    define A_FULL_DIGEST    FLAG(6)    /* full digest            */
  192. ***************
  193. *** 185,190 ****
  194. --- 188,195 ----
  195.   #    define A_ST_FILED    FLAG(10) /* articles is saved        */
  196.   #    define A_ST_REPLY    FLAG(11) /* sent reply to article    */
  197.   #    define A_ST_FOLLOW    FLAG(12) /* sent followup to article    */
  198. + #    define A_CLOSED        FLAG(13) /* subject closed on menu    */
  199. + #    define A_HIDE        FLAG(14) /* hide subject on menu    */
  200.   
  201.   } article_header;
  202.   
  203. *** ./LAST/doc/RELEASE_NOTES    Thu Apr 11 23:42:48 1991
  204. --- doc/RELEASE_NOTES    Thu Apr 11 23:50:53 1991
  205. ***************
  206. *** 1534,1540 ****
  207. --- 1534,1553 ----
  208.   From:    Michael Schwager <schwager@cs.uiuc.edu>
  209.   Fixed:    Patch #14 [newsrc.c]
  210.   
  211. + Prog:    nn
  212. + Title:    Current article can now be select by `.' when asked "... from article"
  213. + From:    farrell@batserver.cs.uq.oz.au (Friendless)
  214. + Fixed:    Patch #15 [menu.c]
  215. +     Specifically, `K CR .'  would not kill the current article.
  216. +     The fix allows `.' {select} everywhere an article id can be entered.
  217. + Prog:    nn
  218. + Title:    Should adjust number of unread articles so "1 of 20/1" is avoided.
  219. + From:    meulenbr@cst.philips.nl (Frans Meulenbroeks)
  220. + Fixed:    Patch #15 [menu.c]
  221.   
  222.   New features since initial 6.4.0 release
  223.   ----------------------------------------
  224.   
  225. ***************
  226. *** 2064,2066 ****
  227. --- 2077,2089 ----
  228.   Added:    Patch #14 [answer.c config.h-dist]
  229.   
  230.       Simply define NNTP_PATH_HOSTNAME as the path of the file.
  231. + Prog:    nn
  232. + Title:    Added "consolidated menus" (one menu line per subject)
  233. + From:    KFS on numerous requests
  234. + Added:    Patch #15 [data.h group.c keymap.c/.h menu.c sort.c variable.c nn.1]
  235. + Prog:    nn
  236. + Title:    Blank lines can now be added between menu lines via `menu-spacing'.
  237. + From:    KFS on request from salsbury@acsu.buffalo.edu (Patrick G. Salsbury)
  238. + Added:    Patch #15 [menu.c variable.c nn.1]
  239. *** ./LAST/folder.c    Wed Feb  6 19:14:17 1991
  240. --- folder.c    Thu Apr 11 22:27:04 1991
  241. ***************
  242. *** 503,509 ****
  243.       goto move_back;
  244.       }
  245.   
  246. !     sort_articles(0);
  247.   
  248.       printf("\rCompressing folder...\n\r"); fl;
  249.   
  250. --- 503,509 ----
  251.       goto move_back;
  252.       }
  253.   
  254. !     sort_articles(-2);
  255.   
  256.       printf("\rCompressing folder...\n\r"); fl;
  257.   
  258. *** ./LAST/group.c    Thu Apr 11 22:05:34 1991
  259. --- group.c    Thu Apr 11 22:27:04 1991
  260. ***************
  261. *** 356,362 ****
  262.       if (access_mode & ACC_ALSO_READ_ARTICLES || first_art >= 0) continue;
  263.       if (menu_cmd != ME_NO_ARTICLES) gh->group_flag &= ~G_NEW;
  264.       if (gh->group_flag & G_UNSUBSCRIBED && !keep_unsub_long) continue;
  265. !     if (n) sort_articles(0);
  266.       n = 0;
  267.       update_rc(gh);
  268.       rc_merged_groups_hack = 1;
  269. --- 356,362 ----
  270.       if (access_mode & ACC_ALSO_READ_ARTICLES || first_art >= 0) continue;
  271.       if (menu_cmd != ME_NO_ARTICLES) gh->group_flag &= ~G_NEW;
  272.       if (gh->group_flag & G_UNSUBSCRIBED && !keep_unsub_long) continue;
  273. !     if (n) sort_articles(-2);
  274.       n = 0;
  275.       update_rc(gh);
  276.       rc_merged_groups_hack = 1;
  277. ***************
  278. *** 448,453 ****
  279. --- 448,454 ----
  280.       extern group_header *jump_to_group;
  281.       article_number o_cur_first = -1;
  282.       group_header *read_mode_group;
  283. +     extern int bypass_consolidation;
  284.   
  285.   #define goto_return( cmd ) \
  286.       { menu_cmd = cmd; goto goto_exit; }
  287. ***************
  288. *** 848,853 ****
  289. --- 849,855 ----
  290.       if (ah == NULL) goto_return(ME_NO_REDRAW);
  291.       strncpy(mask, (access_mode & ACC_ON_SUBJECT) ? ah->subject : ah->sender, GET_S_BUFFER);
  292.       mask[GET_S_BUFFER-1] = NUL;
  293. +     bypass_consolidation = 1;
  294.       }
  295.   
  296.       if (*mask) {
  297. ***************
  298. *** 860,865 ****
  299. --- 862,868 ----
  300.       m_endinput();
  301.       if (o_cur_first < 0) o_cur_first = gh->current_first;
  302.       menu_cmd = group_menu(gh, first, access_mode, mask, menu);
  303. +     bypass_consolidation = 0;    /* in case no articles were found */
  304.       release_memory(&mem_marker);
  305.   
  306.    goto_exit:
  307. *** ./LAST/keymap.c    Wed Nov  7 15:54:33 1990
  308. --- keymap.c    Thu Apr 11 22:11:28 1991
  309. ***************
  310. *** 330,337 ****
  311.   /* %   */        K_PREVIEW,
  312.   /* &   */    K_UNBOUND,
  313.   /* '   */    K_UNBOUND,
  314. ! /* (   */    K_UNBOUND,
  315. ! /* )   */    K_UNBOUND,
  316.   /* *   */        K_SELECT_SUBJECT,
  317.   /* +   */        K_AUTO_SELECT,
  318.   /* ,   */        K_NEXT_LINE,
  319. --- 330,337 ----
  320.   /* %   */        K_PREVIEW,
  321.   /* &   */    K_UNBOUND,
  322.   /* '   */    K_UNBOUND,
  323. ! /* (   */        K_OPEN_SUBJECT,
  324. ! /* )   */        K_CLOSE_SUBJECT,
  325.   /* *   */        K_SELECT_SUBJECT,
  326.   /* +   */        K_AUTO_SELECT,
  327.   /* ,   */        K_NEXT_LINE,
  328. ***************
  329. *** 453,458 ****
  330. --- 453,459 ----
  331.       "back-group",        K_BACK_GROUP,        0,
  332.   
  333.       "cancel",            K_CANCEL,        0,
  334. +     "close-subject",        K_CLOSE_SUBJECT,    K_ONLY_MENU,
  335.       "command",            K_EXTENDED_CMD,        0,
  336.       "compress",            K_COMPRESS,        K_ONLY_MORE,
  337.       "continue",            K_CONTINUE,        0,
  338. ***************
  339. *** 490,495 ****
  340. --- 491,497 ----
  341.       "next-subject",        K_NEXT_SUBJECT,        K_ONLY_MORE,
  342.       "nil",            K_UNBOUND,        0,
  343.   
  344. +     "open-subject",        K_OPEN_SUBJECT,        K_ONLY_MENU,
  345.       "overview",            K_GROUP_OVERVIEW,    0,
  346.   
  347.       "page+1",            K_NEXT_PAGE,        0,
  348. *** ./LAST/keymap.h    Mon Jul  9 17:59:56 1990
  349. --- keymap.h    Thu Apr 11 22:11:28 1991
  350. ***************
  351. *** 99,104 ****
  352. --- 99,107 ----
  353.   
  354.   #define K_PREVIEW        0x004f /* preview article         */
  355.   
  356. + #define K_OPEN_SUBJECT        0x0050 /* open subject on menu        */
  357. + #define K_CLOSE_SUBJECT        0x0051 /* close subject on menu        */
  358.   #define    K_EQUAL_KEY        0x0070 /* map command special symbol    */
  359.   
  360.   #define    K_MACRO            0x0100 /* call macro            */
  361. *** ./LAST/man/nn.1.A    Wed Nov  7 15:54:36 1990
  362. --- man/nn.1.A    Thu Apr 11 23:53:53 1991
  363. ***************
  364. *** 352,357 ****
  365. --- 352,367 ----
  366.   typical menu will thus only show each subject once, saving a lot of
  367.   time in scanning the news articles.
  368.   .LP
  369. + If \fIconsolidated menus\fP (see section below) are enabled, adjacent
  370. + articles sharing the same subject will be shown with a \fIsingle\fP
  371. + line on the menu corresponding to the \fIfirst\fP of the articles.
  372. + The number of articles with the same subject will be shown as a
  373. + braketed number in front of the subject, e.g. with layout 1:
  374. + .br
  375. +     \fIx Name.........   123  [4] Subject..............\fP
  376. + .br
  377. + For further information see the section on consolidated menus below.
  378. + .LP
  379.   \fBRelated variables\fP:
  380.   collapse-subject, columns, confirm-entry, confirm-entry-limit,
  381.   entry-report-limit, fsort, kill, layout, limit, lines, long-menu,
  382. ***************
  383. *** 664,669 ****
  384. --- 674,772 ----
  385.   confirm-auto-quit, confirm-entry, confirm-junk-seen,
  386.   marked-by-next-group, marked-by-read-return, marked-by-read-skip,
  387.   retain-seen-status, select-on-sender.
  388. + .SH CONSOLIDATED MENUS
  389. + Normally, \fInn\fP will use one menu line for each article, so if
  390. + there are many articles with identical subjects, each menu page will
  391. + only contain a few different subjects.  To have each subject occur
  392. + only once on the menu, \fInn\fP can operate with consolidated menus by
  393. + setting the variable \fBconsolidated-menu\fP.
  394. + .LP
  395. + When consolidated menus are used, \fInn\fP operates with two kinds of
  396. + subjects: open and closed.
  397. + .LP
  398. + An \fIopen subject\fP is a subject which is shown in the traditional way
  399. + with one menu line for each article with the given subject.  In other
  400. + words, when consolidated menus are not used, all subjects are open (by
  401. + default).
  402. + .LP
  403. + A \fIclosed subject\fP is a multi-article subject which is presented
  404. + by a single menu line.  This line will be the normal menu line for the
  405. + first (oldest) article with the subject, but with the subject field
  406. + annotated with a bracketed number showing the number of articles with
  407. + that subject, e.g.
  408. + .sp 0.5v
  409. +     a Kim F. Storm     12  [4] Future plans for nn
  410. + .br
  411. +     b.Kim F. Storm     43  [3] More plans for nn
  412. + .sp 0.5v
  413. + In this example, there are four unread articles with subject `a' of
  414. + which the first is posted by me and has 12 lines.  The rest of the
  415. + articles are hidden, and will only be shown on request.  The `.'
  416. + marker on subject `b' shows that all three articles within that
  417. + subject have been read (or seen).
  418. + .LP
  419. + To select (or deselect) ALL the articles within a closed subject,
  420. + simply select the article shown on the menu; this will automatically
  421. + select (or deselect) the rest (see auto-select-closed).  When all the
  422. + unread articles within a closed subject are selected, the menu line
  423. + will be high-lighted.
  424. + .LP
  425. + If you want to view the individual articles in a subject (maybe to
  426. + select individual articles), you can open the subject with the
  427. + commands:
  428. + .TP
  429. + \fB(x\fP
  430. + Open subject \fIx\fP on menu.
  431. + .TP
  432. + \fB((\fP
  433. + Open current subject.
  434. + .LP
  435. + When you have completed viewing the opened subject, you can close it
  436. + again using the commands:
  437. + .TP
  438. + \fB)x\fP
  439. + Close subject \fIx\fP on menu (\fIx\fP is any article with the subject).
  440. + .TP
  441. + \fB))\fP
  442. + Close current subject.
  443. + .LP
  444. + In the basic layout of the menu line for a closed subject as shown
  445. + above, ALL articles in the closed subject are supposed to be either:
  446. + .TP
  447. + \fIunread\fP
  448. + The menu line is \fInot\fP high-lighted.
  449. + .TP
  450. + \fIselected\fP
  451. + Menu line is fully high-lighted (if all UNREAD are selected).
  452. + .TP
  453. + \fBread/seen\fP
  454. + There is a `.' (read attribute) following the article id.
  455. + .LP
  456. + If neither of these cases apply, i.e. there is a mixture of unread,
  457. + selected, and seen/read articles, the bracketed number will have one
  458. + of the following formats:
  459. + .TP
  460. + [U:T]
  461. + There are U unread articles of T total (U<T).
  462. + .TP
  463. + [S/T]
  464. + There are S selected articles of T total (S<U=T).
  465. + .TP
  466. + [S/U:T]
  467. + There are S selected of U unread of T total (S<U<T).
  468. + .LP
  469. + If there are any selected articles (S>0), the information between the
  470. + brackets will be high-lighted (to show that something is selected, but
  471. + not all the unread articles).
  472. + .LP
  473. + \fBNotice\fP:  Consolidated menus only work with the `subject' and
  474. + `lexical' sorting methods.
  475. + .LP
  476. + Variables related to consolidated menus are:
  477. + auto-select-closed,
  478. + consolidated-menu,
  479. + counter-delim-left, counter-delim-right,
  480. + save-closed-mode.
  481.   .SH THE JUNK-ARTICLES AND LEAVE-NEXT COMMANDS
  482.   The \fBJ\fP {\fBjunk-articles\fP} command is a very flexible command
  483.   which can perform all sorts of attribute changes, either on individual
  484. *** ./LAST/man/nn.1.C    Thu Apr 11 22:05:37 1991
  485. --- man/nn.1.C    Thu Apr 11 23:53:53 1991
  486. ***************
  487. *** 245,250 ****
  488. --- 245,261 ----
  489.   conditionally if the value is greater than zero and the number of
  490.   unread articles in the current group does not exceed the given value.
  491.   .TP
  492. + \fBauto-select-closed\fP \fImode\fP    (integer, default 1)
  493. + Normally, selecting a \fIclosed subject\fP (usually in consolidated
  494. + menu mode) will select (or deselect) all \fIunread\fP articles with
  495. + the given subject (or all articles if they are all read).  This
  496. + behaviour can be changed via the value of this variable as follows:
  497. + .nf
  498. + 0: select only the first article with the subject (shown on menu).
  499. + 1: select only the unread articles with the subject.
  500. + 2: select all available articles with the subject.
  501. + .fi
  502. + .TP
  503.   \fBauto-select-subject\fP    (boolean, default false)
  504.   When set, selecting an article from the menu using the article id
  505.   (a-z), all articles on the menu with the same subject will
  506. ***************
  507. *** 365,370 ****
  508. --- 376,394 ----
  509.   key.  (It will show the symbol <> to indicate that it is awaiting
  510.   confirmation.)
  511.   .TP
  512. + \fBconsolidated-menu\fP        (boolean, default false)
  513. + When set, \fInn\fP will automatically \fIclose\fP all multi-article
  514. + subjects on entry to a group, so that each subject only occur once on
  515. + the menu page.
  516. + .TP
  517. + \fBcounter-delim-left\fP    (string, default "[")
  518. + The delimiter string output to the left of the article counter in a
  519. + closed subject's menu line.
  520. + .TP
  521. + \fBcounter-delim-right\fP    (string, default "] ")
  522. + The delimiter string output to the right of the article counter in a
  523. + closed subject's menu line.
  524. + .TP
  525.   \fBcross-filter-seq\fP        (boolean, default true)
  526.   When set, cross posted articles will be presented in the first
  527.   possible group, i.e. according to the current presentation sequence
  528. ***************
  529. *** 804,809 ****
  530. --- 828,844 ----
  531.   the underline attribute).  This is typically used to give overlapping
  532.   lines a different colour on terminals which have this capability.
  533.   .TP
  534. + \fBmenu-spacing\fP \fImode\fP    (integer, default 0)
  535. + When \fImode\fP is a non-zero number as described below, \fInn\fP will
  536. + add blank lines between the lines on the menu to increase readability
  537. + at the cost of presenting fewer articles on each page.  The following
  538. + values of \fImode\fP are recognized:
  539. + .nf
  540. + 0: Don't add blank lines between menu lines.
  541. + 1: Add a blank line between articles with \fIdifferent\fP subjects.
  542. + 2: Add a blank line between \fIall\fP articles.
  543. + .fi
  544. + .TP
  545.   \fBmessage-history\fP \fIN\fP    (integer, default 15)
  546.   Specifies the maximum number, \fIN\fP, of older messages which can be
  547.   recalled with the \fB^P\fP {\fBmessage\fP} command.
  548. ***************
  549. *** 1110,1115 ****
  550. --- 1145,1167 ----
  551.   When set, \fInn\fP will try the specified number of \fItimes\fP to
  552.   open an article before reporting that the article does not exist
  553.   any more.  This may be necessary in some network environments.
  554. + .TP
  555. + \fBsave-closed-mode\fP \fImode\fP    (integer, default 13)
  556. + When saving an article in selection mode (i.e. by selecting it from
  557. + the menu), \fInn\fP will simply save the specified article if the
  558. + article's subject is \fIopen\fP.  When the selected menu entry is a
  559. + closed subject, the \fBsave-closed-mode\fP variable determines how
  560. + many articles among the closed articles should be saved:
  561. + .nf
  562. + 0: save root article (the one on the menu) only
  563. + 1: save selected articles within subject
  564. + 2: save unread (excl selected) articles within subject
  565. + 3: save selected+unread articles within subject
  566. + 4: save all articles within subject
  567. + .fi
  568. + If `10' is added to the above values, \fInn\fP will not save the
  569. + selected subject immediately; instead it will ask which articles
  570. + to save using the above value as the default answer.
  571.   .TP
  572.   \fBsave-counter\fP \fIformat\fP    (string, default "%d")
  573.   This is the printf-format which \fInn\fP uses to create substitution
  574. *** ./LAST/menu.c    Thu Jul 19 18:12:19 1990
  575. --- menu.c    Thu Apr 11 22:11:32 1991
  576. ***************
  577. *** 31,36 ****
  578. --- 31,43 ----
  579.   export int  show_purpose_mode = 1; /* 0: never, 1: new, 2: always */
  580.   export int  read_ret_next_page = 0; /* Z returns to next page */
  581.   
  582. + export int  consolidated_menu = 0; /* show only root articles */
  583. + export int  save_closed_mode = 13; /* ask how to save closed subj, dflt all */
  584. + export int  auto_select_closed = 1; /* select all in closed subject */
  585. + export int  menu_spacing = 0;        /* number of screen lines per menu line */
  586. + export char *counter_delim_left  = "[";
  587. + export char *counter_delim_right = "] ";
  588.   export int  auto_preview_mode = 0; /* preview rather than select */
  589.   export int  preview_continuation = 12; /* what to do after preview */
  590.   export int  preview_mark_read = 1; /* previewed articles are A_READ */
  591. ***************
  592. *** 66,71 ****
  593. --- 73,93 ----
  594.   
  595.   char attributes[30] = " .,+=#! **"; /* Corresponds to A_XXXX in data.h */
  596.   
  597. + static int menu_length;        /* current no of line on menu */
  598. + static int menu_articles;    /* current no of articles on menu */
  599. + static struct menu_info {    /* info for each menu line */
  600. +     int mi_cura;    /* cura corresponding to this menu line */
  601. +     int mi_total;    /* total number of articles with this subject */
  602. +     int mi_unread;    /* no of unread articles with this subject */
  603. +     int mi_selected;    /* no of selected articles with this subject */
  604. +     int mi_art_id;    /* article id (for mark()) */
  605. + } menu_info[INTERVAL1+INTERVAL2];
  606. + static struct menu_info *art_id_to_mi[INTERVAL1+INTERVAL2];
  607. + #define IS_VISIBLE(ah)    (((ah)->flag & (A_CLOSED | A_ROOT_ART)) != A_CLOSED)
  608.   prt_replies(level)
  609.   {
  610.       int re;
  611. ***************
  612. *** 107,130 ****
  613.       return 11;
  614.   }
  615.   
  616.   static mark()
  617.   {
  618.       register article_header *ah;
  619.       int lno, lnum, lsubj, lname;
  620.   
  621.       ah = articles[firsta + cura];
  622.       last_attr = ah->attr;
  623.   
  624. -     if (last_attr == ah->disp_attr) return;
  625.       if (cura < 0 || cura > numa) return;
  626.   
  627. !     lno = firstl + cura;
  628. !     if (ah->disp_attr == A_NOT_DISPLAYED) {
  629. !     gotoxy(0, lno);
  630. !     putchar(ident[cura]);
  631.       goto print_line;
  632.       }
  633.   
  634.       /* A_AUTO_SELECT will not occur here! */
  635.   
  636.       if (!slow_mode)
  637. --- 129,250 ----
  638.       return 11;
  639.   }
  640.   
  641. + static article_number root_article(root)
  642. + register article_number root;
  643. + {
  644. +     register article_header *ah = articles[root];
  645. +     if (ah->flag & A_ROOT_ART)
  646. +     return root;
  647. +     if (ah->flag & A_CLOSED)    /* only root article is shown on menu */
  648. +     return firsta + menu_info[ah->menu_line].mi_cura;
  649. +     while (root > 0) {
  650. +     if (articles[root]->flag & A_ROOT_ART) break;
  651. +     root--;
  652. +     }
  653. +     return root;
  654. + }
  655. + static article_number next_root_article(root)
  656. + register article_number root;
  657. + {
  658. +     while (++root < n_articles)
  659. +     if (articles[root]->flag & A_ROOT_ART) break;
  660. +     return root;
  661. + }
  662. + static set_root_if_closed()
  663. + {
  664. +     if (articles[firsta + cura]->flag & A_CLOSED)
  665. +     cura = root_article(firsta + cura) - firsta;
  666. + }
  667. + /*
  668. +  *    count info for thread containing article #art (must be on menu!)
  669. +  *    returns article number for next root article
  670. +  */
  671. + static article_number thread_counters(art)
  672. + article_number art;
  673. + {
  674. +     register struct menu_info *mi;
  675. +     register article_number n;
  676. +     int total, unread, selected, invisible;
  677. +     
  678. +     if (!(articles[art]->flag & A_CLOSED)) return art + 1;
  679. +     
  680. +     total = unread = selected = 0;
  681. +     n = art = root_article(art);
  682. +     while (n < n_articles) {
  683. +     if (articles[n]->attr == 0) unread++;
  684. +     else if (articles[n]->attr & A_SELECT) selected++; 
  685. +     total++;
  686. +     if (++n == n_articles) break;
  687. +     if (articles[n]->flag & A_ROOT_ART) break;
  688. +     }
  689. +     unread += selected;
  690. +     mi = menu_info + articles[art]->menu_line;
  691. +     mi->mi_total = total;
  692. +     mi->mi_unread = unread;
  693. +     mi->mi_selected = selected;
  694. +     return n;
  695. + }
  696. + static cursor_at_id()
  697. + {
  698. +     gotoxy(0, firstl + articles[firsta + cura]->menu_line);
  699. +     fl; /* place cursor at current article id */
  700. +     save_xy();
  701. + }
  702.   static mark()
  703.   {
  704.       register article_header *ah;
  705. +     register struct menu_info *mi;
  706.       int lno, lnum, lsubj, lname;
  707. +     int only_counters;
  708. +     char cbuf[80];
  709. +     attr_type cattr;
  710.   
  711.       ah = articles[firsta + cura];
  712. +     if (!IS_VISIBLE(ah)) return;
  713. +     
  714.       last_attr = ah->attr;
  715. +     if (ah->disp_attr == A_NOT_DISPLAYED) {
  716. +     mi = &menu_info[ah->menu_line];
  717. +     lno = firstl + ah->menu_line;
  718. +     gotoxy(0, lno);
  719. +     putchar(ident[mi->mi_art_id]);
  720. +     only_counters = 0;
  721. +     goto print_line;
  722. +     }
  723.   
  724.       if (cura < 0 || cura > numa) return;
  725.   
  726. !     lno = firstl + ah->menu_line;
  727. !     if (ah->flag & A_CLOSED) {
  728. !     struct menu_info old;
  729. !     mi = &menu_info[ah->menu_line];
  730. !     old = *mi;
  731. !     thread_counters(firsta + cura);
  732. !     if (old.mi_total == mi->mi_total &&
  733. !         old.mi_selected == mi->mi_selected &&
  734. !         old.mi_unread == mi->mi_unread) return;
  735. !     if (old.mi_total == old.mi_selected)
  736. !         only_counters = mi->mi_total == mi->mi_selected;
  737. !     else
  738. !         only_counters = mi->mi_total != mi->mi_selected;
  739.       goto print_line;
  740.       }
  741.   
  742. +     if (last_attr == ah->disp_attr) return;
  743.       /* A_AUTO_SELECT will not occur here! */
  744.   
  745.       if (!slow_mode)
  746. ***************
  747. *** 150,163 ****
  748.          4    id   subject  (or as 1 if short subject)
  749.        */
  750.   
  751.       if (fmt_linenum > 4) fmt_linenum = 1;
  752.   
  753. !     if (!slow_mode && (ah->attr & A_SELECT)) {
  754.       if (so_gotoxy(1, lno, 1) == 0)
  755.           putchar(attributes[A_SELECT]);
  756.       } else {
  757.           gotoxy(1, lno);
  758. !     putchar(attributes[ah->attr]);
  759.       }
  760.   
  761.       if (ah->lines <    10) lnum = 1; else
  762. --- 270,307 ----
  763.          4    id   subject  (or as 1 if short subject)
  764.        */
  765.   
  766. +     cattr = ah->attr;
  767. +     cbuf[0] = NUL;
  768. +     if (ah->flag & A_CLOSED) {
  769. +     char sel[10], unr[10];
  770. +     if (mi->mi_unread == 0)
  771. +         cattr = A_READ;
  772. +     else if (mi->mi_total == mi->mi_selected)
  773. +         cattr = A_SELECT;
  774. +     else if (auto_select_closed == 1 && mi->mi_unread == mi->mi_selected)
  775. +         cattr = A_SELECT;
  776. +     else if (mi->mi_selected)
  777. +         cattr = A_KILL;    /* pseudo flag -> highlight cbuf */
  778. +     else
  779. +         cattr = 0;
  780. +     sel[0] = unr[0] = NUL;
  781. +     if (mi->mi_selected && mi->mi_selected < mi->mi_unread)
  782. +         sprintf(sel, "%d/", mi->mi_selected);
  783. +     if (mi->mi_unread && mi->mi_unread < mi->mi_total)
  784. +         sprintf(unr, "%d:", mi->mi_unread);
  785. +     sprintf(cbuf, "%s%s%d", sel, unr, mi->mi_total);
  786. +     }
  787.       if (fmt_linenum > 4) fmt_linenum = 1;
  788.   
  789. !     if (!slow_mode && (cattr & A_SELECT)) {
  790.       if (so_gotoxy(1, lno, 1) == 0)
  791.           putchar(attributes[A_SELECT]);
  792.       } else {
  793.           gotoxy(1, lno);
  794. !     putchar(cattr == A_KILL ? ' ' : attributes[cattr]);
  795.       }
  796.   
  797.       if (ah->lines <    10) lnum = 1; else
  798. ***************
  799. *** 205,210 ****
  800. --- 349,364 ----
  801.       break;
  802.       }
  803.   
  804. +  print_subj:
  805. +     if (cbuf[0]) {
  806. +     so_printf("%s", counter_delim_left);
  807. +     if (cattr == A_KILL) so_gotoxy(-1, -1, 0);
  808. +     so_printf("%s", cbuf);
  809. +     if (cattr == A_KILL) so_end();
  810. +     so_printf("%s", counter_delim_right);
  811. +     lsubj -= strlen(cbuf) + strlen(counter_delim_left) + strlen(counter_delim_right);
  812. +     }
  813. +     
  814.       if (!fmt_rptsubj && lno > firstl && ah->flag & A_SAME) {
  815.       if (ah->replies == 0 || prt_replies(ah->replies) == 0)
  816.           so_printf("-");
  817. ***************
  818. *** 227,232 ****
  819. --- 381,387 ----
  820.       so_printf(ah->lines >= 0 ? " +%d" : " +?", ah->lines);
  821.   
  822.       so_end();
  823. +     if (ah->flag & A_CLOSED) clrline();
  824.   
  825.    out:
  826.       ah->disp_attr = last_attr;
  827. ***************
  828. *** 280,288 ****
  829. --- 435,448 ----
  830.       if (!kill_file_loaded && !init_kill()) return 0;
  831.   
  832.       o_cura = cura;
  833. +     cura = -1;
  834.   
  835.       for (i = 0, ahp = articles; i < n_articles; i++, ahp++) {
  836.       ah = *ahp;
  837. +     if (cura >= 0 && (ah->flag & A_ROOT_ART)) {
  838. +         mark();
  839. +         cura = -1;
  840. +     }
  841.       if (re != NULL) {
  842.           if (!regexec_cf(re, select_on_sender ? ah->sender : ah->subject)) continue;
  843.       } else
  844. ***************
  845. *** 290,302 ****
  846.   
  847.       count++;
  848.       if (ah->attr & A_SELECT) continue;
  849.       if (firsta <= i && i <= (firsta+numa)) {
  850.           cura = i - firsta;
  851. !         new_mark(A_SELECT);
  852. !     } else
  853. !         ah->attr = A_SELECT;
  854.       }
  855.   
  856.       if (count)
  857.       msg("Selected %d article%s", count, plural((long)count));
  858.       else
  859. --- 450,467 ----
  860.   
  861.       count++;
  862.       if (ah->attr & A_SELECT) continue;
  863. +     ah->attr = A_SELECT;
  864.       if (firsta <= i && i <= (firsta+numa)) {
  865.           cura = i - firsta;
  866. !         if ((ah->flag & A_CLOSED) == 0) {
  867. !         mark();
  868. !         cura = -1;
  869. !         }
  870. !     }
  871.       }
  872.   
  873. +     if (cura >= 0) mark();
  874.       if (count)
  875.       msg("Selected %d article%s", count, plural((long)count));
  876.       else
  877. ***************
  878. *** 320,326 ****
  879.        case 1:
  880.       return 0;
  881.        case 2:
  882. !     return (articles[firsta+cura]->flag & (A_SAME | A_ALMOST_SAME)) == 0;
  883.       }
  884.       return 0;
  885.   }
  886. --- 485,491 ----
  887.        case 1:
  888.       return 0;
  889.        case 2:
  890. !     return articles[firsta+cura]->flag & A_ROOT_ART;
  891.       }
  892.       return 0;
  893.   }
  894. ***************
  895. *** 404,410 ****
  896.            case MC_NEXTSUBJ:
  897.           ah->attr = A_READ;
  898.           for (next = cur+1; next < n_articles; next++) {
  899. !             if (((ah = articles[next])->flag & (A_SAME | A_ALMOST_SAME)) == 0) break;
  900.               ah->attr = A_READ;
  901.           }
  902.           for (; next < n_articles; next++) {
  903. --- 569,575 ----
  904.            case MC_NEXTSUBJ:
  905.           ah->attr = A_READ;
  906.           for (next = cur+1; next < n_articles; next++) {
  907. !             if ((ah = articles[next])->flag & A_ROOT_ART) break;
  908.               ah->attr = A_READ;
  909.           }
  910.           for (; next < n_articles; next++) {
  911. ***************
  912. *** 416,422 ****
  913.           ah->attr = A_READ;
  914.           for (next = cur+1; next < n_articles; next++) {
  915.               ah = articles[next];
  916. !             if ((ah->flag & (A_SAME | A_ALMOST_SAME)) == 0) break;
  917.               ah->attr = A_SELECT;
  918.           }
  919.           for (next = cur+1; next < n_articles; next++)
  920. --- 581,587 ----
  921.           ah->attr = A_READ;
  922.           for (next = cur+1; next < n_articles; next++) {
  923.               ah = articles[next];
  924. !             if (ah->flag & A_ROOT_ART) break;
  925.               ah->attr = A_SELECT;
  926.           }
  927.           for (next = cur+1; next < n_articles; next++)
  928. ***************
  929. *** 494,499 ****
  930. --- 659,665 ----
  931.   
  932.   static int article_id;
  933.   static int cur_key;
  934. + static int is_k_select;    /* set when K_ARTICLE_ID was really K_SELECT */
  935.   
  936.   static int get_k_cmd_1()
  937.   {
  938. ***************
  939. *** 522,535 ****
  940.       goto loop;
  941.       }
  942.   
  943.       if (map & K_ARTICLE_ID) {
  944.       article_id = map & ~K_ARTICLE_ID;
  945.       map = K_ARTICLE_ID;
  946.   
  947. !     if (article_id < 0 || article_id > numa) {
  948.           ding();
  949.           goto loop;
  950.       }
  951.       }
  952.   
  953.       if (any_message && map != K_LAST_MESSAGE) clrmsg(-1);
  954. --- 688,708 ----
  955.       goto loop;
  956.       }
  957.   
  958. +     is_k_select = 0;
  959. +     if (map == K_SELECT) {
  960. +     map = K_ARTICLE_ID;
  961. +     article_id = cura;
  962. +     is_k_select = 1;
  963. +     } else
  964.       if (map & K_ARTICLE_ID) {
  965.       article_id = map & ~K_ARTICLE_ID;
  966.       map = K_ARTICLE_ID;
  967.   
  968. !     if (article_id < 0 || article_id > menu_articles) {
  969.           ding();
  970.           goto loop;
  971.       }
  972. +     article_id = art_id_to_mi[article_id]->mi_cura;
  973.       }
  974.   
  975.       if (any_message && map != K_LAST_MESSAGE) clrmsg(-1);
  976. ***************
  977. *** 575,591 ****
  978.   int update;
  979.   {
  980.       int any;
  981.   
  982.       if (new == old) return 0;
  983.       if (new == A_KILL) update = 0;
  984.   
  985.       any = 0;
  986.       while (first < last) {
  987. !     if (old == A_KILL || articles[first]->attr == old) {
  988. !         articles[first]->attr = new;
  989. !         if (update) {
  990. !         cura = first-firsta;
  991. !         mark();
  992.           }
  993.           any = 1;
  994.       }
  995. --- 748,776 ----
  996.   int update;
  997.   {
  998.       int any;
  999. +     register article_header *ah;
  1000. +     article_number ocura = cura;
  1001.   
  1002.       if (new == old) return 0;
  1003.       if (new == A_KILL) update = 0;
  1004.   
  1005.       any = 0;
  1006. +     cura = -1;
  1007.       while (first < last) {
  1008. !     ah = articles[first];
  1009. !     if (cura >= 0 && ah->flag & A_ROOT_ART) {
  1010. !         set_root_if_closed();
  1011. !         mark();
  1012. !         cura = -1;
  1013. !     }
  1014. !     if (old == A_KILL || ah->attr == old) {
  1015. !         ah->attr = new;
  1016. !         if (update && first >= firsta && first < nexta) {
  1017. !         cura = first - firsta;
  1018. !         if ((ah->flag & A_CLOSED) == 0) {
  1019. !             mark();
  1020. !             cura = -1;
  1021. !         }
  1022.           }
  1023.           any = 1;
  1024.       }
  1025. ***************
  1026. *** 592,600 ****
  1027. --- 777,798 ----
  1028.   
  1029.       first++;
  1030.       }
  1031. +     if (cura >= 0) {
  1032. +     set_root_if_closed();
  1033. +     mark();
  1034. +     }
  1035. +     cura = update ? last - firsta : ocura;
  1036.       return any;
  1037.   }
  1038.   
  1039. + static repl_attr_subject(old, new, update)
  1040. + attr_type old, new;
  1041. + int update;
  1042. + {
  1043. +     return repl_attr(root_article(firsta+cura), next_root_article(firsta+cura),
  1044. +              old, new, update);
  1045. + }
  1046.   
  1047.   static repl_attr_all(old, new, update)
  1048.   attr_type old, new;
  1049. ***************
  1050. *** 631,636 ****
  1051. --- 829,878 ----
  1052.       }
  1053.   }
  1054.   
  1055. + /*
  1056. +  * bypass_consolidation may be set to temporarily overrule the global
  1057. +  * consolidated_menu variable:
  1058. +  *    1:    don't consolidate    (e.g. for G=...  )
  1059. +  *    2:    do consolidate
  1060. +  *    3:    use consolidated_mode
  1061. +  */
  1062. + export int bypass_consolidation = 0;
  1063. + static int cur_bypass = 0;    /* current bypass status (see below) */
  1064. + static do_consolidation()
  1065. + {
  1066. +     int consolidate;
  1067. +     switch (bypass_consolidation) {
  1068. +      case 0:
  1069. +     break;
  1070. +      case 1:
  1071. +     cur_bypass = -1;    /* no consolidation */
  1072. +     break;
  1073. +      case 2:
  1074. +     cur_bypass = 1;        /* force consolidation */
  1075. +     break;
  1076. +      case 3:
  1077. +     cur_bypass = 0;        /* reset bypass to use consolidated_menu */
  1078. +     break;
  1079. +     }
  1080. +     bypass_consolidation = 0;
  1081. +     if (cur_bypass)
  1082. +     consolidate = cur_bypass > 0;
  1083. +     else
  1084. +     consolidate = consolidated_menu;
  1085. +     if (consolidate)
  1086. +     for (nexta = 0; nexta < n_articles; nexta++)
  1087. +         articles[nexta]->flag |= A_CLOSED;
  1088. +     else
  1089. +     for (nexta = 0; nexta < n_articles; nexta++)
  1090. +         articles[nexta]->flag &= ~A_CLOSED;
  1091. +     return consolidate;
  1092. + }
  1093.   
  1094.   menu(print_header)
  1095.   fct_type print_header;
  1096. ***************
  1097. *** 637,642 ****
  1098. --- 879,886 ----
  1099.   {
  1100.       register         k_cmd, cur_k_cmd;
  1101.       register        article_header *ah;
  1102. +     register struct menu_info *mi;
  1103. +     int            consolidate, o_bypass;
  1104.       int            last_k_cmd;
  1105.       int         menu_cmd, temp;
  1106.       int         save_selected;
  1107. ***************
  1108. *** 670,675 ****
  1109. --- 914,925 ----
  1110.       menu_level++;
  1111.   
  1112.       if (menu_level == 1) {
  1113. +     if ((current_group->group_flag & G_COUNTED)
  1114. +         && n_articles != current_group->unread_count) {
  1115. +         add_unread(current_group, -1);
  1116. +         current_group->unread_count = n_articles;
  1117. +         add_unread(current_group, 0);
  1118. +     }
  1119.       entry_check = conf_group_entry && n_articles > conf_entry_limit;
  1120.       auto_read = auto_read_limit < 0 || n_articles <= auto_read_limit;
  1121.       } else {
  1122. ***************
  1123. *** 691,698 ****
  1124. --- 941,954 ----
  1125.        case 0: break;
  1126.        case 1: if ((current_group->group_flag & G_NEW) == 0) break;
  1127.        case 2: get_purpose(purpose);
  1128. +              if (purpose[0]) strcpy(delayed_msg, purpose);
  1129.       }
  1130.   
  1131. +     o_bypass = cur_bypass;
  1132. +     cur_bypass = 0;
  1133. +     
  1134. +     consolidate = do_consolidation();
  1135.       firsta = 0;
  1136.       while (firsta < n_articles && articles[firsta]->attr == A_SEEN)
  1137.       firsta++;
  1138. ***************
  1139. *** 768,800 ****
  1140.       gotoxy(0, firstl);
  1141.       clrpage(firstl);
  1142.   
  1143. !     if (nexta > 0) {
  1144. !     firsta = nexta;
  1145. !     } else
  1146. !     if (purpose[0]) {
  1147. !         msg(purpose);
  1148. !     }
  1149.       firsta = nexta;
  1150. -     numa = Lines; /* for mark; is set correctly below */
  1151.       cura = 0;
  1152.   
  1153.       REDRAW_CHECK;
  1154.   
  1155. !     if (!s_keyboard)
  1156. !     while (nexta < n_articles && cura < maxa) {
  1157.           REDRAW_CHECK;
  1158.   
  1159. !         articles[firsta+cura]->disp_attr = A_NOT_DISPLAYED;
  1160.           mark();
  1161. !         nexta++; cura++;
  1162.       }
  1163.   
  1164.       fl;
  1165.       s_keyboard = 0;
  1166.   
  1167. !     prompt_line = firstl + cura;
  1168. !     if (!long_menu || cura < maxa) prompt_line++;
  1169.   
  1170.       numa = nexta - firsta - 1;
  1171.        if (numa < 0) prompt_line++;
  1172. --- 1024,1106 ----
  1173.       gotoxy(0, firstl);
  1174.       clrpage(firstl);
  1175.   
  1176. !     if (articles[nexta]->flag & A_CLOSED)
  1177. !     nexta = root_article(nexta);
  1178.       firsta = nexta;
  1179.       cura = 0;
  1180.   
  1181.       REDRAW_CHECK;
  1182.   
  1183. !     menu_length = 0;
  1184. !     menu_articles = 0;
  1185. !     goto draw_menu;
  1186. !     
  1187. !  partial_redraw:
  1188. !     next_cura = cura;
  1189. !  partial_redraw_nc:
  1190. !     nexta = firsta + cura;
  1191. !     menu_length = articles[nexta]->menu_line;
  1192. !     menu_articles = menu_info[menu_length].mi_art_id;
  1193. !     gotoxy(0, firstl + menu_length);
  1194. !     clrpage(firstl + menu_length);
  1195. !  draw_menu:
  1196. !     if (!s_keyboard) {
  1197. !     int first_menu_line = menu_length;
  1198. !     mi = menu_info + menu_length;
  1199. !     while (nexta < n_articles) {
  1200.           REDRAW_CHECK;
  1201.   
  1202. !         ah = articles[nexta];
  1203. !         if (ah->flag & A_HIDE) {
  1204. !         ah->menu_line = menu_length;    /* just in case.... */
  1205. !         continue;
  1206. !         }
  1207. !         if (menu_length > first_menu_line) {
  1208. !         switch (menu_spacing) {
  1209. !          case 0:
  1210. !             break;
  1211. !          case 1:
  1212. !             if ((ah->flag & A_ROOT_ART) == 0) break;
  1213. !          case 2:
  1214. !             menu_length++;
  1215. !             mi++;
  1216. !             break;
  1217. !         }
  1218. !         if (menu_length >= maxa) break;
  1219. !         }
  1220. !         
  1221. !         ah->menu_line = menu_length;
  1222. !         art_id_to_mi[menu_articles] = mi;
  1223. !         mi->mi_art_id = menu_articles++;
  1224. !         
  1225. !         mi->mi_cura = cura = nexta - firsta;
  1226. !         if (ah->flag & A_CLOSED) {    /* skip rest of thread */
  1227. !         nexta = thread_counters(nexta);
  1228. !         if (nexta == firsta + cura + 1)
  1229. !             ah->flag &= ~A_CLOSED;
  1230. !         else {
  1231. !             article_number tmpa = firsta + cura;
  1232. !             while (++tmpa < nexta)
  1233. !             articles[tmpa]->menu_line = menu_length;
  1234. !         }
  1235. !         } else
  1236. !         nexta++;
  1237. !         ah->disp_attr = A_NOT_DISPLAYED;
  1238.           mark();
  1239. !         if (++menu_length >= maxa) break;
  1240. !         mi++;
  1241.       }
  1242. +     }
  1243.   
  1244. +     if (menu_length > maxa) menu_length = maxa;
  1245.       fl;
  1246.       s_keyboard = 0;
  1247.   
  1248. !     prompt_line = firstl + menu_length;
  1249. !     if (!long_menu || menu_length < maxa) prompt_line++;
  1250.   
  1251.       numa = nexta - firsta - 1;
  1252.        if (numa < 0) prompt_line++;
  1253. ***************
  1254. *** 801,811 ****
  1255. --- 1107,1123 ----
  1256.   
  1257.        if (next_cura >= 0) {
  1258.        cura = next_cura;
  1259. +      set_root_if_closed();
  1260.        next_cura = -1;
  1261.        } else {
  1262.        cura = 0;
  1263.        for (article_id = firsta; cura < numa; article_id++, cura++)
  1264. +      {
  1265.            if ((articles[article_id]->attr & A_SELECT) == 0) break;    /*???*/
  1266. +          if (!IS_VISIBLE(articles[article_id])) continue;
  1267. +      }
  1268. +      if (!IS_VISIBLE(articles[article_id]))
  1269. +          cura = root_article(article_id) - firsta;
  1270.        }
  1271.   
  1272.     build_prompt:
  1273. ***************
  1274. *** 824,835 ****
  1275.   
  1276.     same_prompt:
  1277.   
  1278.        if (cura < 0 || cura > numa) cura = 0;
  1279.   
  1280.        if (numa >= 0) {
  1281. !      gotoxy(0, firstl + cura);
  1282. !      fl; /* place cursor at current article id */
  1283. !      save_xy();
  1284.        }
  1285.   
  1286.        last_k_cmd = cur_k_cmd;
  1287. --- 1136,1148 ----
  1288.   
  1289.     same_prompt:
  1290.   
  1291. +     if (!IS_VISIBLE(articles[firsta+cura]))
  1292. +     cura = next_root_article(firsta + cura) - firsta;
  1293.        if (cura < 0 || cura > numa) cura = 0;
  1294.   
  1295.        if (numa >= 0) {
  1296. !      cursor_at_id();
  1297.        }
  1298.   
  1299.        last_k_cmd = cur_k_cmd;
  1300. ***************
  1301. *** 874,879 ****
  1302. --- 1187,1193 ----
  1303.        goto same_prompt;
  1304.   
  1305.         case K_EXTENDED_CMD:
  1306. +      temp = consolidated_menu;
  1307.        switch (alt_command()) {
  1308.   
  1309.         case AC_UNCHANGED:
  1310. ***************
  1311. *** 887,894 ****
  1312.   
  1313.         case AC_REORDER:
  1314.            firsta = 0;
  1315. !          /* fall thru */
  1316.         case AC_REDRAW:
  1317.            goto redraw;
  1318.   
  1319.         case AC_KEYCMD:
  1320. --- 1201,1212 ----
  1321.   
  1322.         case AC_REORDER:
  1323.            firsta = 0;
  1324. !          consolidate = do_consolidation();
  1325. !          goto redraw;
  1326.         case AC_REDRAW:
  1327. +          if (temp != consolidated_menu)
  1328. +          consolidate = do_consolidation();
  1329.            goto redraw;
  1330.   
  1331.         case AC_KEYCMD:
  1332. ***************
  1333. *** 944,950 ****
  1334.            last_save = firsta + numa;
  1335.            } else
  1336.            if (k_cmd == K_AUTO_SELECT) {
  1337. !          save_selected = 2;
  1338.            cura = -firsta;
  1339.            article_id = 0;
  1340.            last_save = n_articles - 1;
  1341. --- 1262,1268 ----
  1342.            last_save = firsta + numa;
  1343.            } else
  1344.            if (k_cmd == K_AUTO_SELECT) {
  1345. !          save_selected = 1;
  1346.            cura = -firsta;
  1347.            article_id = 0;
  1348.            last_save = n_articles - 1;
  1349. ***************
  1350. *** 953,964 ****
  1351.            cura = article_id;
  1352.            article_id += firsta;
  1353.            last_save = article_id;
  1354.            } else
  1355.            break;
  1356.   
  1357.            for ( ; article_id <= last_save ; article_id++, cura++) {
  1358.            ah = articles[article_id];
  1359. !          if (save_selected && (ah->attr & A_SELECT) == 0) continue;
  1360.   
  1361.            if (cur_k_cmd == K_CANCEL) {
  1362.                if (current_group->group_flag & G_FOLDER) {
  1363. --- 1271,1337 ----
  1364.            cura = article_id;
  1365.            article_id += firsta;
  1366.            last_save = article_id;
  1367. +          if (articles[article_id]->flag & A_CLOSED) {
  1368. +              int n = save_closed_mode % 10;
  1369. +              int c;
  1370. +              
  1371. +              if (article_id == n_articles - 1) 
  1372. +              goto save_it;
  1373. +              if (articles[article_id + 1]->flag & A_ROOT_ART)
  1374. +              goto save_it;
  1375. +              if (save_closed_mode >= 10) {
  1376. +              prompt("\1%s thread\1 (r)oot (s)elected (u)nread (b)oth (a)ll  (%c)",
  1377. +                 savemode, "rsuba"[n]);
  1378. +              switch (get_c()) {
  1379. +               case 'r':    n = 0; break;
  1380. +               case 's':    n = 1; break;
  1381. +               case 'u':    n = 2; break;
  1382. +               case 'b':    n = 3; break;
  1383. +               case 'a':    n = 4; break;
  1384. +               case SP:
  1385. +               case CR:
  1386. +               case NL:    break;
  1387. +               default:    ding(); goto Prompt;
  1388. +              }
  1389. +              }
  1390. +              switch (n) {
  1391. +               case 0:        /* save root only */
  1392. +              break;
  1393. +               case 1:        /* save all selected */
  1394. +              save_selected = 1;
  1395. +              break;
  1396. +               case 2:        /* save all unread */
  1397. +              save_selected = 2;
  1398. +              break;
  1399. +               case 3:        /* save all selected + unread */
  1400. +              save_selected = 3;
  1401. +              break;
  1402. +               case 4:         /* save all articles */
  1403. +              break;
  1404. +              }
  1405. +              if (n) last_save = next_root_article(article_id) - 1;
  1406. +              save_selected |= 8;    /* closed subject */
  1407. +              temp1 = cura;
  1408. +          }
  1409.            } else
  1410.            break;
  1411.   
  1412. +       save_it:
  1413.            for ( ; article_id <= last_save ; article_id++, cura++) {
  1414.            ah = articles[article_id];
  1415. !          switch (save_selected & 0x3) {
  1416. !           case 0:
  1417. !              break;
  1418. !           case 3:
  1419. !              if (ah->attr == 0) break;
  1420. !           case 1:
  1421. !              if ((ah->attr & A_SELECT) == 0) continue;
  1422. !              break;
  1423. !           case 2:
  1424. !              if (ah->attr == 0) break;
  1425. !              continue;
  1426. !          }             
  1427.   
  1428.            if (cur_k_cmd == K_CANCEL) {
  1429.                if (current_group->group_flag & G_FOLDER) {
  1430. ***************
  1431. *** 999,1004 ****
  1432. --- 1372,1382 ----
  1433.                mark();
  1434.            }
  1435.            }
  1436. +          if (save_selected & 8) {
  1437. +          save_selected = 0;    /* select closed */
  1438. +          cura = temp1;
  1439. +          mark();
  1440. +          }
  1441.        }
  1442.   
  1443.        if (save_selected) cura = 0;
  1444. ***************
  1445. *** 1149,1159 ****
  1446. --- 1527,1540 ----
  1447.        /* FALL THRU */
  1448.   
  1449.         case K_GOTO_GROUP:
  1450. +      temp1 = n_articles;
  1451.   
  1452.        switch (goto_group(k_cmd, (article_header *)NULL, (flag_type)0)) {
  1453.   
  1454.         case ME_REDRAW:
  1455.            firsta = 0;
  1456. +          if (temp1 != n_articles && consolidate)
  1457. +          consolidate = do_consolidation();
  1458.            goto redraw;
  1459.   
  1460.         case ME_NO_ARTICLES:
  1461. ***************
  1462. *** 1173,1178 ****
  1463. --- 1554,1618 ----
  1464.            goto empty_menu_hack;
  1465.        }
  1466.   
  1467. +       case K_OPEN_SUBJECT:
  1468. +      if (numa < 0) goto nextmenu;
  1469. +      prompt("\1Open subject\1");
  1470. +      k_cmd = get_k_cmd();
  1471. +      if (k_cmd != K_ARTICLE_ID) {
  1472. +          if (k_cmd != cur_k_cmd) goto Prompt;
  1473. +          article_id = cura;
  1474. +      }
  1475. +       open_subject:
  1476. +      ah = articles[firsta+article_id];
  1477. +      if (!(ah->flag & A_CLOSED) ||
  1478. +          (ah->flag & (A_ROOT_ART | A_NEXT_SAME)) == A_ROOT_ART)
  1479. +          goto Prompt;
  1480. +      cura = article_id = root_article(firsta + article_id) - firsta;
  1481. +      while (cura + firsta < n_articles) {
  1482. +          ah = articles[firsta+cura];
  1483. +          if (cura > article_id && ah->flag & A_ROOT_ART)
  1484. +          break;
  1485. +          ah->flag &= ~A_CLOSED;
  1486. +          cura++;
  1487. +      }
  1488. +      cura = article_id;
  1489. +      goto partial_redraw;
  1490. +      
  1491. +       case K_CLOSE_SUBJECT:
  1492. +      if (numa < 0) goto nextmenu;
  1493. +      prompt("\1Close subject\1");
  1494. +      k_cmd = get_k_cmd();
  1495. +      if (k_cmd != K_ARTICLE_ID) {
  1496. +          if (k_cmd != cur_k_cmd) goto Prompt;
  1497. +          article_id = cura;
  1498. +      }
  1499. +      ah = articles[firsta+article_id];
  1500. +      if ((ah->flag & A_CLOSED) ||
  1501. +          (ah->flag & (A_ROOT_ART | A_NEXT_SAME)) == A_ROOT_ART) {
  1502. +          cura = next_root_article(firsta + cura) - firsta;
  1503. +          goto Prompt;
  1504. +      }
  1505. +      cura = article_id = root_article(firsta + article_id) - firsta;
  1506. +      while (cura + firsta < n_articles) {
  1507. +          ah = articles[firsta+cura];
  1508. +          if (cura > article_id && ah->flag & A_ROOT_ART)
  1509. +          break;
  1510. +          ah->flag |= A_CLOSED;
  1511. +          cura++;
  1512. +      }
  1513. +      cura = article_id;
  1514. +      next_cura = next_root_article(firsta + cura) - firsta;
  1515. +      if (cura >= 0) goto partial_redraw_nc;
  1516. +      articles[cura + firsta]->menu_line = articles[firsta]->menu_line;
  1517. +      firsta += cura;
  1518. +      goto redraw;
  1519.         case K_LEAVE_NEXT:
  1520.         case K_JUNK_ARTICLES:
  1521.        junk_prompt = cur_k_cmd == K_JUNK_ARTICLES ? 1 : 5;
  1522. ***************
  1523. *** 1207,1213 ****
  1524.   
  1525.         junk_another:
  1526.            if (cura < 0 || cura > numa) cura = 0;
  1527. !          gotoxy(0, firstl + cura); fl;
  1528.   
  1529.            switch (get_k_cmd()) {
  1530.             case K_JUNK_ARTICLES:
  1531. --- 1647,1653 ----
  1532.   
  1533.         junk_another:
  1534.            if (cura < 0 || cura > numa) cura = 0;
  1535. !          cursor_at_id();
  1536.   
  1537.            switch (get_k_cmd()) {
  1538.             case K_JUNK_ARTICLES:
  1539. ***************
  1540. *** 1216,1226 ****
  1541.   
  1542.             case K_ARTICLE_ID:
  1543.            cura = article_id;
  1544. -           case K_SELECT:
  1545.            if (junk_attr == A_KILL) junk_attr = A_READ;
  1546. !          articles[firsta + cura]->attr = junk_attr;
  1547. !          mark();
  1548. !          cura++;
  1549.            goto junk_another;
  1550.   
  1551.             case K_NEXT_LINE:
  1552. --- 1656,1669 ----
  1553.   
  1554.             case K_ARTICLE_ID:
  1555.            cura = article_id;
  1556.            if (junk_attr == A_KILL) junk_attr = A_READ;
  1557. !          if (auto_select_closed > 0 && articles[firsta + cura]->flag & A_CLOSED)
  1558. !              repl_attr_subject(A_KILL, junk_attr, 1);
  1559. !          else {
  1560. !              articles[firsta + cura]->attr = junk_attr;
  1561. !              mark();
  1562. !              cura++;
  1563. !          }
  1564.            goto junk_another;
  1565.   
  1566.             case K_NEXT_LINE:
  1567. ***************
  1568. *** 1312,1325 ****
  1569.         case K_ARTICLE_ID:
  1570.        if (numa < 0) goto nextmenu;
  1571.   
  1572. !      if (auto_preview_mode) goto auto_preview;
  1573.   
  1574.        cura = article_id;
  1575. !      toggle();
  1576. !      if (auto_select_subject) goto select_subject;
  1577. !      mark();
  1578. !      cura++;
  1579.   
  1580.        goto same_prompt;
  1581.   
  1582.         case K_SELECT_INVERT:
  1583. --- 1755,1783 ----
  1584.         case K_ARTICLE_ID:
  1585.        if (numa < 0) goto nextmenu;
  1586.   
  1587. !      if (!is_k_select && auto_preview_mode) goto auto_preview;
  1588.   
  1589.        cura = article_id;
  1590. !      if (!auto_select_closed || !(articles[firsta+cura]->flag & A_CLOSED)) {
  1591. !          toggle();
  1592. !          if (!is_k_select && auto_select_subject) goto select_subject;
  1593. !          mark();
  1594. !          cura++;
  1595. !          goto same_prompt;
  1596. !      }
  1597. !      
  1598. !      if (auto_select_closed < 0) {
  1599. !          article_id = cura;
  1600. !          goto open_subject;
  1601. !      }
  1602.   
  1603. +      mi = menu_info + articles[firsta+cura]->menu_line;
  1604. +      if (mi->mi_unread == 0)
  1605. +          repl_attr_subject(A_KILL, A_SELECT, 1);
  1606. +      else if (mi->mi_selected == mi->mi_unread)
  1607. +          repl_attr_subject(auto_select_closed == 2 ? A_KILL : A_SELECT, 0, 1);
  1608. +      else
  1609. +          repl_attr_subject(auto_select_closed == 2 ? A_KILL : 0, A_SELECT, 1);
  1610.        goto same_prompt;
  1611.   
  1612.         case K_SELECT_INVERT:
  1613. ***************
  1614. *** 1330,1336 ****
  1615.        no_raw();    /* for x-on/x-off */
  1616.        for (cura = 0; cura <= numa; cura++) {
  1617.            toggle();
  1618. !          mark();
  1619.        }
  1620.        fl;
  1621.   
  1622. --- 1788,1796 ----
  1623.        no_raw();    /* for x-on/x-off */
  1624.        for (cura = 0; cura <= numa; cura++) {
  1625.            toggle();
  1626. !      }
  1627. !      for (cura = 0; cura <= numa; cura++) {
  1628. !          if (IS_VISIBLE(articles[firsta+cura])) mark();
  1629.        }
  1630.        fl;
  1631.   
  1632. ***************
  1633. *** 1340,1354 ****
  1634.        cura = temp;
  1635.        goto same_prompt;
  1636.   
  1637. -       case K_SELECT:
  1638. -      if (numa < 0) goto nextmenu;
  1639. -      toggle();
  1640. -      mark();
  1641. -      cura++;
  1642. -      goto same_prompt;
  1643.         case K_UNSELECT_ALL:
  1644.        if (last_k_cmd == K_UNSELECT_ALL)
  1645.            repl_attr_all(A_SELECT, 0, 1);
  1646. --- 1800,1805 ----
  1647. ***************
  1648. *** 1368,1398 ****
  1649.        if (numa < 0) goto nextmenu;
  1650.   
  1651.        if (--cura < 0) cura = numa;
  1652.        goto same_prompt;
  1653.   
  1654.         case K_SELECT_SUBJECT:
  1655.        if (numa < 0) goto nextmenu;
  1656.   
  1657. !      if (last_k_cmd != K_ARTICLE_ID && last_k_cmd != K_SELECT)
  1658.            toggle();
  1659.   
  1660.         select_subject:
  1661. !      while (firsta+cura > 0 &&
  1662. !         (articles[firsta+cura]->flag & (A_SAME | A_ALMOST_SAME)))
  1663. !          cura--;
  1664. !      do {
  1665. !          new_mark(last_attr);
  1666. !          cura++;
  1667. !          if (firsta+cura >= n_articles) break;
  1668. !      } while (articles[firsta+cura]->flag & (A_SAME | A_ALMOST_SAME));
  1669.        goto same_prompt;
  1670.   
  1671.         case K_SELECT_RANGE:
  1672.        if (numa < 0) goto nextmenu;
  1673.   
  1674. !      if (last_k_cmd == K_ARTICLE_ID || last_k_cmd == K_SELECT) {
  1675.            cura--;
  1676.            if (cura < 0) cura = numa;
  1677.        } else
  1678. --- 1819,1841 ----
  1679.        if (numa < 0) goto nextmenu;
  1680.   
  1681.        if (--cura < 0) cura = numa;
  1682. +      set_root_if_closed();
  1683.        goto same_prompt;
  1684.   
  1685.         case K_SELECT_SUBJECT:
  1686.        if (numa < 0) goto nextmenu;
  1687.   
  1688. !      if (last_k_cmd != K_ARTICLE_ID)
  1689.            toggle();
  1690.   
  1691.         select_subject:
  1692. !      repl_attr_subject(A_KILL, last_attr, 1);
  1693.        goto same_prompt;
  1694.   
  1695.         case K_SELECT_RANGE:
  1696.        if (numa < 0) goto nextmenu;
  1697.   
  1698. !      if (last_k_cmd == K_ARTICLE_ID) {
  1699.            cura--;
  1700.            if (cura < 0) cura = numa;
  1701.        } else
  1702. ***************
  1703. *** 1410,1431 ****
  1704.   
  1705.        if (k_cmd != K_ARTICLE_ID) goto Prompt;
  1706.   
  1707. !      if (last_k_cmd != K_ARTICLE_ID && last_k_cmd != K_SELECT)
  1708. !          new_mark(last_attr);
  1709. !      if (article_id <= cura) {
  1710. !          while (cura >= article_id) {
  1711. !          new_mark(last_attr);
  1712. !          cura--;
  1713. !          }
  1714. !          if (cura < 0) cura = 0;
  1715. !      } else {
  1716. !          while (cura <= article_id) {
  1717. !          new_mark(last_attr);
  1718. !          cura++;
  1719. !          }
  1720. !          if (cura > numa) cura = numa;
  1721.        }
  1722.        goto Prompt;
  1723.   
  1724.         case K_AUTO_SELECT:
  1725. --- 1853,1865 ----
  1726.   
  1727.        if (k_cmd != K_ARTICLE_ID) goto Prompt;
  1728.   
  1729. !      if (article_id > cura) {
  1730. !          article_number tmp = cura;
  1731. !          cura  = article_id;
  1732. !          article_id = tmp;
  1733.        }
  1734. +      repl_attr(article_id, cura, A_KILL, last_attr, 1);
  1735.        goto Prompt;
  1736.   
  1737.         case K_AUTO_SELECT:
  1738. ***************
  1739. *** 1460,1467 ****
  1740.         case K_PREV_PAGE:
  1741.        if (firsta == 0 && nexta == n_articles) goto same_prompt;
  1742.   
  1743. !      nexta = (firsta > 0 ? firsta : n_articles) - maxa;
  1744. !      if (nexta <= 1) nexta = 0;
  1745.        goto nextmenu;
  1746.   
  1747.         case K_FIRST_PAGE:
  1748. --- 1894,1917 ----
  1749.         case K_PREV_PAGE:
  1750.        if (firsta == 0 && nexta == n_articles) goto same_prompt;
  1751.   
  1752. !       prevmenu:
  1753. !      nexta = (firsta > 0 ? firsta : n_articles);
  1754. !      
  1755. !      for (menu_length = maxa; menu_length > 0 && --nexta >= 0; ) {
  1756. !         if (!IS_VISIBLE(articles[nexta])) continue;
  1757. !         if (--menu_length > 0) {
  1758. !         switch (menu_spacing) {
  1759. !          case 0:
  1760. !             break;
  1761. !          case 1:
  1762. !             if ((articles[nexta]->flag & A_ROOT_ART) == 0) break;
  1763. !          case 2:
  1764. !             --menu_length;
  1765. !             break;
  1766. !         }
  1767. !         }
  1768. !      }
  1769. !      if (nexta < 0) nexta = 0;
  1770.        goto nextmenu;
  1771.   
  1772.         case K_FIRST_PAGE:
  1773. ***************
  1774. *** 1472,1481 ****
  1775.   
  1776.         case K_LAST_PAGE:
  1777.        if (nexta == n_articles) goto same_prompt;
  1778. !      nexta = n_articles - maxa;
  1779. !      if (nexta <= 1) nexta = 0;
  1780. !      goto nextmenu;
  1781.   
  1782.         case K_PREVIEW:
  1783.        if (numa < 0) goto nextmenu;
  1784. --- 1922,1929 ----
  1785.   
  1786.         case K_LAST_PAGE:
  1787.        if (nexta == n_articles) goto same_prompt;
  1788. !      firsta = 0;
  1789. !      goto prevmenu;
  1790.   
  1791.         case K_PREVIEW:
  1792.        if (numa < 0) goto nextmenu;
  1793. ***************
  1794. *** 1616,1621 ****
  1795. --- 2064,2070 ----
  1796.   
  1797.    menu_exit:
  1798.   
  1799. +     cur_bypass = o_bypass;
  1800.       n_selected = o_selected;
  1801.       firsta = o_firsta;
  1802.       in_menu_mode = o_mode;
  1803. *** ./LAST/newsrc.c    Thu Apr 11 22:05:41 1991
  1804. --- newsrc.c    Thu Apr 11 22:27:03 1991
  1805. ***************
  1806. *** 1010,1016 ****
  1807.   
  1808.   /*
  1809.    *    Update .newsrc for one group.
  1810. !  *    sort_articles(0) MUST HAVE BEEN CALLED BEFORE USE.
  1811.    */
  1812.   
  1813.   export int rc_merged_groups_hack = 0;
  1814. --- 1010,1016 ----
  1815.   
  1816.   /*
  1817.    *    Update .newsrc for one group.
  1818. !  *    sort_articles(-2) MUST HAVE BEEN CALLED BEFORE USE.
  1819.    */
  1820.   
  1821.   export int rc_merged_groups_hack = 0;
  1822. *** ./LAST/patchlevel.h    Thu Apr 11 22:05:41 1991
  1823. --- patchlevel.h    Thu Apr 11 22:11:32 1991
  1824. ***************
  1825. *** 25,31 ****
  1826.    *    1990-11-07: Patch #12 (6.4.12) - LOW
  1827.    *    1991-02-06: Patch #13 (6.4.13) - MEDIUM
  1828.    *    1991-03-22: Patch #14 (6.4.14) - MEDIUM
  1829.    */
  1830.   
  1831. ! #define PATCHLEVEL 14
  1832.   
  1833. --- 25,32 ----
  1834.    *    1990-11-07: Patch #12 (6.4.12) - LOW
  1835.    *    1991-02-06: Patch #13 (6.4.13) - MEDIUM
  1836.    *    1991-03-22: Patch #14 (6.4.14) - MEDIUM
  1837. +  *    1991-04-02: Patch #15 (6.4.15) - LOW
  1838.    */
  1839.   
  1840. ! #define PATCHLEVEL 15
  1841.   
  1842. *** ./LAST/sort.c    Fri Oct  5 19:07:19 1990
  1843. --- sort.c    Thu Apr 11 23:05:09 1991
  1844. ***************
  1845. *** 209,226 ****
  1846.       register long n;
  1847.       register flag_type same;
  1848.       fct_type cmp;
  1849.   
  1850.       for (n = n_articles; --n >= 0;)
  1851. !     articles[n]->flag &= ~(A_SAME|A_ALMOST_SAME|A_NEXT_SAME);
  1852.   
  1853. !     if (n_articles <= 1) return;
  1854.   
  1855.       if (mode == -1) mode = sort_mode;
  1856.   
  1857.       switch (mode) {
  1858.        default:
  1859.        case 0:            /* arrival (no sort) */
  1860.       cmp = order_arrival;
  1861.       break;
  1862.        case 1:            /* date-subject-date */
  1863.        case 2:            /* subject-date */
  1864. --- 209,236 ----
  1865.       register long n;
  1866.       register flag_type same;
  1867.       fct_type cmp;
  1868. +     extern int bypass_consolidation;
  1869.   
  1870. +     if (n_articles <= 0) return;
  1871.       for (n = n_articles; --n >= 0;)
  1872. !     articles[n]->flag &= ~(A_SAME|A_ALMOST_SAME|A_NEXT_SAME|A_ROOT_ART);
  1873.   
  1874. !     if (n_articles == 1) {
  1875. !     articles[0]->flag |= A_ROOT_ART;
  1876. !     return;
  1877. !     }
  1878.   
  1879.       if (mode == -1) mode = sort_mode;
  1880.   
  1881.       switch (mode) {
  1882. +      case -2:            /* restore original ordering for update */
  1883. +     cmp = order_arrival;
  1884. +     break;
  1885.        default:
  1886.        case 0:            /* arrival (no sort) */
  1887.       cmp = order_arrival;
  1888. +     bypass_consolidation = 1;
  1889.       break;
  1890.        case 1:            /* date-subject-date */
  1891.        case 2:            /* subject-date */
  1892. ***************
  1893. *** 228,236 ****
  1894. --- 238,248 ----
  1895.       break;
  1896.        case 3:            /* date only */
  1897.       cmp = order_date;
  1898. +     bypass_consolidation = 1;
  1899.       break;
  1900.        case 4:            /* sender-date */
  1901.       cmp = order_from_date;
  1902. +     bypass_consolidation = 1;
  1903.       break;
  1904.       }
  1905.   
  1906. ***************
  1907. *** 237,242 ****
  1908. --- 249,255 ----
  1909.       quicksort(articles, n_articles, article_header *, cmp);
  1910.   
  1911.       articles[0]->root_t_stamp = articles[0]->t_stamp;
  1912. +     articles[0]->flag |= A_ROOT_ART;
  1913.       for (n = n_articles - 1, app = articles + 1; --n >= 0; app++) {
  1914.       if (same = article_equal(app, app - 1)) {
  1915.           app[0]->root_t_stamp = app[-1]->root_t_stamp;
  1916. ***************
  1917. *** 244,249 ****
  1918. --- 257,263 ----
  1919.           app[-1]->flag |= A_NEXT_SAME;
  1920.       } else {
  1921.           app[0]->root_t_stamp = app[0]->t_stamp;
  1922. +         app[0]->flag |= A_ROOT_ART;
  1923.       }
  1924.       }
  1925.   
  1926. ***************
  1927. *** 304,315 ****
  1928.       if (changed && n_articles > 0) {
  1929.       srca = articles;
  1930.       srca[0]->flag &= ~(A_SAME|A_ALMOST_SAME|A_NEXT_SAME);
  1931.       for (n = n_articles - 1, srca++; --n >= 0; srca++) {
  1932. !         srca[0]->flag &= ~(A_SAME|A_ALMOST_SAME|A_NEXT_SAME);
  1933.           if (same = article_equal(srca, srca - 1)) {
  1934.           srca[0]->flag |= same;
  1935.           srca[-1]->flag |= A_NEXT_SAME;
  1936. !         }
  1937.       }
  1938.       }
  1939.   
  1940. --- 318,331 ----
  1941.       if (changed && n_articles > 0) {
  1942.       srca = articles;
  1943.       srca[0]->flag &= ~(A_SAME|A_ALMOST_SAME|A_NEXT_SAME);
  1944. +     srca[0]->flag |= A_ROOT_ART;
  1945.       for (n = n_articles - 1, srca++; --n >= 0; srca++) {
  1946. !         srca[0]->flag &= ~(A_SAME|A_ALMOST_SAME|A_NEXT_SAME|A_ROOT_ART);
  1947.           if (same = article_equal(srca, srca - 1)) {
  1948.           srca[0]->flag |= same;
  1949.           srca[-1]->flag |= A_NEXT_SAME;
  1950. !         } else
  1951. !         srca[0]->flag |= A_ROOT_ART;
  1952.       }
  1953.       }
  1954.   
  1955. *** ./LAST/variable.c    Wed Feb  6 19:14:23 1991
  1956. --- variable.c    Thu Apr 11 22:11:33 1991
  1957. ***************
  1958. *** 32,37 ****
  1959. --- 32,38 ----
  1960.    *            Automatically expanded.  ("unset" set var to NULL).
  1961.    *    STR 3        Ordinary string, but cannot be "unset".
  1962.    *    STR 4        (Expanded) file name - cannot be unset.
  1963. +  *    STR 5        Ordinary string ("unset" set var to "").
  1964.    *
  1965.    *    CODES n        String initialized by list of key names.
  1966.    *            A maximum of 16 CODES variables (n = 0 - 15) exist.
  1967. ***************
  1968. *** 63,68 ****
  1969. --- 64,71 ----
  1970.       *backup_folder_path,
  1971.       *bak_suffix,
  1972.       *bug_address,
  1973. +     *counter_delim_left,
  1974. +     *counter_delim_right,
  1975.       *decode_header_file,
  1976.       *default_distribution,
  1977.       *default_save_file,
  1978. ***************
  1979. *** 119,124 ****
  1980. --- 122,128 ----
  1981.       conf_dont_sleep,
  1982.       conf_group_entry,
  1983.       conf_junk_seen,
  1984. +     consolidated_menu,
  1985.       delay_redraw,
  1986.       dflt_kill_select,
  1987.       do_kill_handling,
  1988. ***************
  1989. *** 186,191 ****
  1990. --- 190,196 ----
  1991.       also_read_articles,
  1992.       article_limit,
  1993.       auto_read_limit,
  1994. +     auto_select_closed,
  1995.       check_db_update,
  1996.       conf_entry_limit,
  1997.       collapse_subject,
  1998. ***************
  1999. *** 204,209 ****
  2000. --- 209,215 ----
  2001.       mark_next_group,
  2002.       mark_read_return,
  2003.       mark_read_skip,
  2004. +     menu_spacing,
  2005.       message_history,
  2006.       min_pv_window,
  2007.       multi_key_guard_time,
  2008. ***************
  2009. *** 218,223 ****
  2010. --- 224,230 ----
  2011.       re_layout_more,
  2012.       response_check_pause,
  2013.       retry_on_error,
  2014. +     save_closed_mode,
  2015.       save_counter_offset,
  2016.       scroll_last_lines,
  2017.       show_purpose_mode,
  2018. ***************
  2019. *** 291,296 ****
  2020. --- 298,304 ----
  2021.       "auto-junk-seen",        BOOL 0,        (char **)&auto_junk_seen,
  2022.       "auto-preview-mode",    BOOL 0,        (char **)&auto_preview_mode,
  2023.       "auto-read-mode-limit",    INT 0,        (char **)&auto_read_limit,
  2024. +     "auto-select-closed",    INT 0,        (char **)&auto_select_closed,
  2025.       "auto-select-subject",    BOOL 0,        (char **)&auto_select_subject,
  2026.       "backup",            BOOL 0,        (char **)&keep_rc_backup,
  2027.       "backup-folder-path",    STR 4,        (char **)&backup_folder_path,
  2028. ***************
  2029. *** 311,316 ****
  2030. --- 319,327 ----
  2031.       "confirm-entry-limit",    INT 0,        (char **)&conf_entry_limit,
  2032.       "confirm-junk-seen",     BOOL 0,        (char **)&conf_junk_seen,
  2033.       "confirm-messages",        BOOL 0,        (char **)&conf_dont_sleep,
  2034. +     "consolidated-menu",    BOOL 1,        (char **)&consolidated_menu,
  2035. +     "counter-delim-left",    STR 5,        (char **)&counter_delim_left,
  2036. +     "counter-delim-right",    STR 5,        (char **)&counter_delim_right,
  2037.       "cross-filter-seq",        BOOL 0,        (char **)&seq_cross_filtering,
  2038.       "cross-post",        BOOL 0,        (char **)&also_cross_postings,
  2039.       "data-bits",        INT 0,        (char **)&data_bits,
  2040. ***************
  2041. *** 375,380 ****
  2042. --- 386,392 ----
  2043.       "marked-by-next-group",    INT 0,        (char **)&mark_next_group,
  2044.       "marked-by-read-return",    INT 0,        (char **)&mark_read_return,
  2045.       "marked-by-read-skip",    INT 0,        (char **)&mark_read_skip,
  2046. +     "menu-spacing",        INT 1,        (char **)&menu_spacing,
  2047.       "message-history",        INT 0,        (char **)&message_history,
  2048.       "min-window",        INT 1,        (char **)&min_pv_window,
  2049.       "mmdf-format",        BOOL 0,        (char **)&use_mmdf_folders,
  2050. ***************
  2051. *** 419,424 ****
  2052. --- 431,437 ----
  2053.       "response-default-answer",    STR 0,        (char **)&response_dflt_answer,
  2054.       "retain-seen-status",     BOOL 0,        (char **)&retain_seen_status,
  2055.       "retry-on-error",        INT 0,        (char **)&retry_on_error,
  2056. +     "save-closed-mode",        INT 0,        (char **)&save_closed_mode,
  2057.       "save-counter",        STR 3,        (char **)&save_counter_format,
  2058.       "save-counter-offset",    INT 0,        (char **)&save_counter_offset,
  2059.       "save-header-lines",    STR 0,        (char **)&save_header_lines,
  2060. ***************
  2061. *** 622,627 ****
  2062. --- 635,644 ----
  2063.           }
  2064.           STR_VAR = copy_str(val_string);
  2065.           break;
  2066. +      case 5:
  2067. +         STR_VAR = (on && val_string) ? copy_str(val_string) : "";
  2068. +         break;
  2069.       }
  2070.       break;
  2071.   
  2072.